package com.ionehe.group.common.uaa.ex.domain.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import com.ionehe.group.common.io.Response;
import com.ionehe.group.common.sms.service.VerificationCodeService;
import com.ionehe.group.common.uaa.ex.convert.UserInfoConvert;
import com.ionehe.group.common.uaa.ex.dao.LoginInfoDao;
import com.ionehe.group.common.uaa.ex.dao.UserInfoDao;
import com.ionehe.group.common.uaa.ex.dao.UserRoleDao;
import com.ionehe.group.common.uaa.ex.domain.service.CustomerReadService;
import com.ionehe.group.common.uaa.ex.domain.service.CustomerWriteService;
import com.ionehe.group.common.uaa.ex.domain.service.UaaCommonService;
import com.ionehe.group.common.uaa.ex.domain.service.UserCacheWriteService;
import com.ionehe.group.common.uaa.ex.domain.service.UserReadService;
import com.ionehe.group.common.uaa.ex.domain.service.UserWriteService;
import com.ionehe.group.common.uaa.ex.dto.CustomerInfoDTO;
import com.ionehe.group.common.uaa.ex.dto.LoginInfoDTO;
import com.ionehe.group.common.uaa.ex.dto.UserChangePasswordDTO;
import com.ionehe.group.common.uaa.ex.dto.UserDetailDTO;
import com.ionehe.group.common.uaa.ex.dto.UserInfoAddDTO;
import com.ionehe.group.common.uaa.ex.dto.UserInfoDTO;
import com.ionehe.group.common.uaa.ex.dto.UserInfoUpdateDTO;
import com.ionehe.group.common.uaa.ex.dto.UserLoginDTO;
import com.ionehe.group.common.uaa.ex.enums.ErrorMessages;
import com.ionehe.group.common.uaa.ex.enums.UserStatus;
import com.ionehe.group.common.uaa.ex.exception.BusinessException;
import com.ionehe.group.common.uaa.ex.model.UaaLoginInfo;
import com.ionehe.group.common.uaa.ex.model.UaaUserInfo;
import com.ionehe.group.common.uaa.ex.model.UaaUserRole;
import com.ionehe.group.common.uaa.ex.utils.LoginUtils;
import com.ionehe.group.common.uaa.ex.utils.RedisUtils;
import com.ionehe.group.common.user.c.sdk.uaa.utils.JwtTokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import tk.mybatis.mapper.entity.Example;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @USER:秀
 * @DATE: 2020/11/30
 * @PROJECT_NAME: general-permissions
 * @Description 用户写服务类
 **/
@Service
@Slf4j
public class UserWriteServiceImpl implements UserWriteService {
    @Autowired
    private UserReadService userReadService;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    private UserCacheWriteService userCacheWriteService;
    @Autowired
    private LoginUtils loginUtil;
    @Autowired(required = false)
    private UserInfoConvert userInfoConvert;
    @Autowired(required = false)
    private UserInfoDao userInfoDao;
    @Autowired(required = false)
    private UserRoleDao userRoleDao;
    @Autowired(required = false)
    private LoginInfoDao loginInfoDao;
    @Autowired
    private UaaCommonService uaaCommonService;
    @Value("${redis.database:base}")
    private String redisDatabase;
    @Value("${redis.key.authCode:uaa:authCode}")
    private String redisKeyAuthCode;
    @Autowired(required = false)
    private VerificationCodeService verificationCodeService;
    @Autowired
    private CustomerWriteService customerWriteService;
    @Autowired
    private CustomerReadService customerReadService;


    @Override
    public Response<String> login(UserLoginDTO userLoginDTO) {
        log.info("UserWriteService[]login[]start! userLoginDTO:{}", userLoginDTO);
        String token;
        try {
            String account = userLoginDTO.getTerminal() + "_" + userLoginDTO.getUsername();
            // 验证码校验
            verificationCodeCheck(userLoginDTO.getKey(), userLoginDTO.getCode());
            //密码需要客户端加密后传递
            Response<UserDetails> userRes = userReadService.loadUserByUsernameAndAccountType(account, userLoginDTO.getType());
            if (!userRes.success()) {
                return Response.no(userRes.getMessage());
            }
            UserDetails userDetails = userRes.getData();
            if (!passwordEncoder.matches(userLoginDTO.getPassword(), userDetails.getPassword())) {
                return Response.no("用户名或密码不正确");
            }
            if (!userDetails.isEnabled()) {
                return Response.no("帐号已被禁用");
            }
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authentication);
            token = jwtTokenUtil.generateToken(userDetails);
            loginUtil.tokenUpperLimitCheck(account, token);
            Response<UserDetailDTO> res = loginUtil.getCurrentUser();
            if (!res.success()) {
                log.error("UserWriteService[]login[]get the current user failed");
                return Response.no(ErrorMessages.USER_LOGIN_FAIL.getErrMsg());
            }
            // 刷新登陆时间
            Long customerId = res.getData().getUserInfoDTO().getCustomerId();
            if (customerId != 0) {
                customerWriteService.updateLastLoginTime(customerId);
            }
            return Response.yes(token);
        } catch (AuthenticationException | BusinessException e) {
            log.warn("UserWriteService[]login[]error! cause:{}", e.getMessage());
            return Response.no(e.getMessage());
        } catch (Exception e) {
            log.error("UserWriteService[]login[]error! cause:{}", Throwables.getStackTraceAsString(e));
            return Response.no(ErrorMessages.USER_LOGIN_FAIL.getErrCode(), ErrorMessages.USER_LOGIN_FAIL.getErrMsg());
        }
    }

    @Override
    public Response<Boolean> add(UserInfoAddDTO userInfoAddDTO) {
        UaaUserInfo userInfoQuery = new UaaUserInfo();
        userInfoQuery.setPhone(userInfoAddDTO.getPhone());
        userInfoQuery.setTerminal(userInfoAddDTO.getTerminal());
        userInfoQuery.setIsDelete(false);
        List<UaaUserInfo> userInfoList = userInfoDao.select(userInfoQuery);
        if (!CollectionUtils.isEmpty(userInfoList)) {
            log.warn("手机号已被注册!");
            return Response.no("手机号已被注册！");
        }
        List<Long> roleInfoIdList = userInfoAddDTO.getRoleInfoIdList();
        if (CollectionUtils.isEmpty(roleInfoIdList)) {
            log.warn("角色为空，创建普通用户");
        }
        UaaUserInfo userInfo = userInfoConvert.dto2do(userInfoAddDTO);
        //userInfo.setStatus(UserStatus.FREEZE.getValue());
        userInfo.setTerminal(userInfo.getTerminal());
        userInfo.setType(1);
        userInfo.setIsDelete(false);
        userInfo.setCreateTime(new Date());
        userInfo.setAvatar("https://rd-demo.oss-cn-hangzhou.aliyuncs.com/01112012ajf.JPG");
        userInfoDao.insertSelective(userInfo);
        //新增登陆信息
        UaaLoginInfo loginInfo = new UaaLoginInfo();
        loginInfo.setPassword(passwordEncoder.encode(userInfoAddDTO.getInitialPassword()));
        loginInfo.setAccount(userInfo.getPhone());
        loginInfo.setUserId(userInfo.getId());
        loginInfo.setAlreadyFirstLogin(false);
        loginInfo.setCreateTime(new Date());
        loginInfo.setIsDelete(false);
        loginInfoDao.insertSelective(loginInfo);

        if (CollUtil.isNotEmpty(roleInfoIdList)) {
            List<UaaUserRole> userRoleList = new ArrayList<>(roleInfoIdList.size());
            roleInfoIdList.forEach(roleInfoId -> {
                UaaUserRole userRole = new UaaUserRole();
                userRole.setRoleId(roleInfoId);
                userRole.setUserId(userInfo.getId());
                userRole.setCreateTime(new Date());
                userRoleList.add(userRole);
            });
            userRoleDao.insertList(userRoleList);
        }

        return Response.yes(true);
    }

    @Override
    public Response<Boolean> update(UserInfoUpdateDTO userInfoUpdateDTO) {
        UaaUserInfo userInfoQuery = userInfoDao.selectByPrimaryKey(userInfoUpdateDTO.getId());
        if (userInfoQuery == null) {
            return Response.no("用户不存在！");
        }
        // 如果是修改状态更新完直接返回
        Integer status = userInfoUpdateDTO.getStatus();
        Integer oldStatus = userInfoQuery.getStatus();
        if (Objects.nonNull(status) && !Objects.equals(status, oldStatus)) {
            userInfoQuery.setStatus(status);
            userInfoDao.updateByPrimaryKeySelective(userInfoQuery);
            cleanCache(userInfoQuery.getPhone());
            return Response.yes(true);
        }
        String oldPhone = userInfoQuery.getPhone();
        userInfoQuery = new UaaUserInfo();
        userInfoQuery.setPhone(userInfoUpdateDTO.getPhone());
        userInfoQuery.setTerminal(userInfoQuery.getTerminal());
        userInfoQuery.setIsDelete(false);
        List<UaaUserInfo> userInfoList = userInfoDao.select(userInfoQuery);
        if ((!userInfoUpdateDTO.getPhone().equals(userInfoQuery.getPhone())) && !CollectionUtils.isEmpty(userInfoList)) {
            return Response.no("手机号已被注册！");
        }

        UaaUserInfo userInfo = userInfoConvert.dto2do(userInfoUpdateDTO);
        userInfoDao.updateByPrimaryKeySelective(userInfo);
        //登陆信息
        Example example = new Example(UaaLoginInfo.class);
        example.createCriteria().andEqualTo("userId", userInfo.getId());
        List<UaaLoginInfo> loginInfoList = loginInfoDao.selectByExample(example);
        if (CollectionUtils.isEmpty(loginInfoList)) {
            return Response.no("登陆信息为空！");
        }
        UaaLoginInfo loginInfo = loginInfoList.get(0);
        loginInfo.setAccount(userInfo.getPhone());
        loginInfoDao.updateByPrimaryKey(loginInfo);
        UaaUserRole userRole = new UaaUserRole();
        userRole.setUserId(userInfoUpdateDTO.getId());
        List<UaaUserRole> userRoleList = userRoleDao.select(userRole);
        if (CollUtil.isNotEmpty(userRoleList)) {
            //传入角色id列表
            List<Long> roleInfoIdList = userInfoUpdateDTO.getRoleInfoIdList();
            if (CollectionUtils.isEmpty(roleInfoIdList)) {
                return Response.no("角色不能为空！");
            }

            Map<Long, Long> roleIdIdMap = userRoleList.stream().collect(Collectors.toMap(UaaUserRole::getRoleId, UaaUserRole::getId));
            //数据库角色id列表
            List<Long> oldRoleIdList = userRoleList.stream().map(UaaUserRole::getRoleId).collect(Collectors.toList());
            //需要删除的
            List<Long> deleteRoleIdList = new ArrayList(oldRoleIdList);
            deleteRoleIdList.removeAll(roleInfoIdList);
            //需要新增的
            List<Long> insertRoleIdList = new ArrayList(roleInfoIdList);
            insertRoleIdList.removeAll(oldRoleIdList);
            //删除数据
            if (!CollectionUtils.isEmpty(deleteRoleIdList)) {
                List<Long> idList = new ArrayList(deleteRoleIdList.size());
                deleteRoleIdList.forEach(roleId -> {
                    Long id = roleIdIdMap.get(roleId);
                    if (id != null) {
                        idList.add(id);
                    }
                });
                userRoleDao.deleteByIdList(idList);
            }
            //新增数据
            if (!CollectionUtils.isEmpty(insertRoleIdList)) {
                List<UaaUserRole> insertUserRoleList = new ArrayList(insertRoleIdList.size());
                insertRoleIdList.forEach(roleId -> {
                    UaaUserRole insertUserRole = new UaaUserRole();
                    insertUserRole.setRoleId(roleId);
                    insertUserRole.setUserId(userInfoUpdateDTO.getId());
                    insertUserRole.setCreateTime(new Date());
                    insertUserRoleList.add(insertUserRole);
                });
                userRoleDao.insertList(insertUserRoleList);
            }
        }

        cleanCache(oldPhone);
        return Response.yes();
    }

    @Override
    public Response<Boolean> delete(Long id) {
        UaaUserInfo userInfo = userInfoDao.selectByPrimaryKey(id);
        if (userInfo == null) {
            return Response.no("用户不存在！");
        }
        userInfoDao.deleteByPrimaryKey(id);
        UaaUserRole userRole = new UaaUserRole();
        userRole.setUserId(id);
        userRoleDao.delete(userRole);
        UaaLoginInfo loginInfoQuery = new UaaLoginInfo();
        loginInfoQuery.setUserId(id);
        loginInfoDao.delete(loginInfoQuery);

        cleanCache(userInfo.getPhone());
        return Response.yes();
    }


    @Override
    public Response<Boolean> logout(String token, String ip) {
        Response<UserInfoDTO> userInfoRes = uaaCommonService.getUserByToken(token);
        if (!userInfoRes.success() || userInfoRes.getData() == null) {
            return Response.no(ErrorMessages.GET_USER_INFO_FAIL.toString());
        }
        return loginUtil.logout(token);
    }

    @Override
    public Response<Boolean> changePassword(UserChangePasswordDTO userChangePasswordDTO) {
        try {
            // 密码长度校验
            passwordLengthCheck(userChangePasswordDTO.getPassword());
            String mobile = userChangePasswordDTO.getUsername();
            Integer terminal = userChangePasswordDTO.getTerminal();
            String smsVerificationCode = userChangePasswordDTO.getSmsVerificationCode();
            String password = userChangePasswordDTO.getPassword();
            String oldPassword = userChangePasswordDTO.getOldPassword();

            // 用户信息
            Response<UserDetailDTO> userDetailsResponse = userReadService.getUserByUsername(terminal + "_" + mobile);
            if (!userDetailsResponse.success()) {
                return Response.no(userDetailsResponse.getMessage());
            }
            if (Objects.isNull(userDetailsResponse.getData())) {
                return Response.no(ErrorMessages.USER_IS_NOT_EXIST.getErrMsg());
            }
            UserInfoDTO userInfo = userDetailsResponse.getData().getUserInfoDTO();
            LoginInfoDTO loginInfo = userDetailsResponse.getData().getLoginInfoDTO();

            // 根据cid查type
            Long customerId = userInfo.getCustomerId();
            if (customerId != 0L) {
                Response<CustomerInfoDTO> customerRes = customerReadService.detail(customerId);
                if (!customerRes.success()) {
                    return Response.no(customerRes.getMessage());
                }

                CustomerInfoDTO customer = customerRes.getData();
                if (customer == null) {
                    return Response.no("账号已不存在");
                }

                if (StrUtil.isNotBlank(customer.getType()) && !customer.getType().equals(userChangePasswordDTO.getType())) {
                    return Response.no("账号不存在");
                }
            }

            if (StrUtil.isNotBlank(oldPassword) && !passwordEncoder.matches(oldPassword, loginInfo.getPassword())) {
                throw new BusinessException("旧密码错误");
            }

            // 新密码不能和旧密码相同
            if (passwordEncoder.matches(password, loginInfo.getPassword())) {
                throw new BusinessException(ErrorMessages.USER_PASSWORD_SAME.toString());
            }

            // 验证码 合法性校验
            if (!"8888".equals(smsVerificationCode) && verificationCodeService != null && !verificationCodeService.verify(terminal + "_" + mobile, smsVerificationCode, null)) {
                return Response.no(ErrorMessages.ILLEGAL_VERIFICATION_CODE.toString());
            }

            // 更新用户状态、用户密码
            updateUserStatus(userInfo);
            updateUserPassword(password, loginInfo);

            // 清除缓存
            userCacheWriteService.delUser(terminal + "_" + mobile);
            return Response.yes(true);
        } catch (BusinessException e) {
            log.warn("UserInfoWriteService[]changePassword[]error! cause:{}", Throwables.getStackTraceAsString(e));
            return Response.no(e.getMessage());
        } catch (Exception e) {
            log.error("UserInfoWriteService[]changePassword[]error! cause:{}", Throwables.getStackTraceAsString(e));
            return Response.no(ErrorMessages.UPDATE_USER_PASSWORD_FAIL.toString());
        }
    }

    @Override
    public Response<Boolean> checkPassword(UserChangePasswordDTO userChangePasswordDTO) {
        try {
            // 密码长度校验
            String mobile = userChangePasswordDTO.getUsername();
            Integer terminal = userChangePasswordDTO.getTerminal();
            String password = userChangePasswordDTO.getPassword();

            // 用户信息
            Response<UserDetailDTO> userDetailsResponse = userReadService.getUserByUsername(terminal + "_" + mobile);
            if (!userDetailsResponse.success()) {
                return Response.no(userDetailsResponse.getMessage());
            }
            if (Objects.isNull(userDetailsResponse.getData())) {
                return Response.no(ErrorMessages.USER_IS_NOT_EXIST.getErrMsg());
            }

            LoginInfoDTO loginInfo = userDetailsResponse.getData().getLoginInfoDTO();

            return Response.yes(passwordEncoder.matches(password, loginInfo.getPassword()));
        } catch (Exception e) {
            log.error("UserInfoWriteService[]checkPassword[]error! cause:{}", Throwables.getStackTraceAsString(e));
            return Response.no(ErrorMessages.CHECK_USER_PASSWORD_FAIL.toString());
        }
    }

    /**
     * 更新用户状态为正在使用
     *
     * @param userInfo 用户信息
     */
    private void updateUserStatus(UserInfoDTO userInfo) {
        Integer userStatus = userInfo.getStatus();
        if (!Objects.equals(userStatus, UserStatus.INIT.getValue())) {
            return;
        }
        UaaUserInfo user = new UaaUserInfo();
        user.setStatus(UserStatus.USING.getValue());
        user.setUpdateTime(new Date());
        user.setId(userInfo.getId());
        if (userInfoDao.updateByPrimaryKeySelective(user) != 1) {
            throw new BusinessException(ErrorMessages.UPDATE_USER_STATUS_FAIL.toString());
        }
    }

    /**
     * 密码长度校验
     *
     * @param password 密码
     */
    private void passwordLengthCheck(String password) {
        int passwordLength = password.length();
        if (passwordLength < 6 || passwordLength > 12) {
            throw new BusinessException(ErrorMessages.USER_PASSWORD_LENGTH_ILLEGAL.toString());
        }
    }

    /**
     * 更新用户密码
     *
     * @param password  密码
     * @param loginInfo 原登陆信息
     */
    private void updateUserPassword(String password, LoginInfoDTO loginInfo) {
        loginInfo.setAlreadyFirstLogin(true);
        loginInfo.setPassword(passwordEncoder.encode(password));
        loginInfo.setUpdateTime(new Date());
        if (loginInfoDao.updateByPrimaryKeySelective(userInfoConvert.dto2do(loginInfo)) != 1) {
            throw new BusinessException(ErrorMessages.UPDATE_USER_PASSWORD_FAIL.toString());
        }
    }

    /**
     * 验证码校验
     *
     * @param key  key
     * @param code 验证码
     */
    private void verificationCodeCheck(String key, String code) {
        key = generateKey(key);
        if (!Objects.equals(code, redisUtils.get(0, key)) && !"8888".equals(code)) {
            throw new BusinessException(ErrorMessages.ILLEGAL_VERIFICATION_CODE.toString());
        }
        redisUtils.delete(0, key);
    }

    private String generateKey(String key) {
        return redisDatabase + ":" + redisKeyAuthCode + ":" + key;
    }

    /**
     * 清除redis用户数据
     *
     * @param username 用户名
     */
    private void cleanCache(String username) {
        // 删除缓存
        userCacheWriteService.delUser(username);
        userCacheWriteService.delResourceList(username);
        userCacheWriteService.delUserToken(Lists.newArrayList(username));
    }
}
